using Microsoft.AspNetCore.Authorization;

using OpenIddict.Abstractions;

using Volo.Abp;
using Volo.Abp.Domain.Repositories;
using Volo.Abp.OpenIddict.Applications;

using WL.Think.Abp.OpenIddict.Dtos;
using WL.Think.Abp.OpenIddict.Permission;
using WL.Think.Abp.Services;

namespace WL.Think.Abp.OpenIddict.Services
{
    [Authorize(OpenIddictPermissions.Applications.Default)]
    public class ApplicationAppService : ThinkAppService<OpenIddictApplication, ApplicationDto, ApplicationPagedDto, ApplicationCreateUpdateDto>, IApplicationAppService
    {
        private readonly IOpenIddictApplicationManager _applicationManager;

        public ApplicationAppService(
            IRepository<OpenIddictApplication, Guid> repository, 
            IOpenIddictApplicationManager applicationManager
        ) : base(repository)
        {
            _applicationManager = applicationManager;
        }

        [Authorize(OpenIddictPermissions.Applications.Create)]
        public override async Task<ApplicationDto> CreateAsync(ApplicationCreateUpdateDto input)
        {
            if (await Repository.AnyAsync(x => x.ClientId == input.ClientId!))
            {
                throw new UserFriendlyException($"客户端ID已经存在：{input.ClientId}");
            }
            var descriptor = new AbpApplicationDescriptor
            {
                ClientId = input.ClientId,
                ClientType = input.ClientType,
                ClientSecret = input.ClientSecret,
                ConsentType = input.ConsentType,
                DisplayName = input.DisplayName,
                ApplicationType = input.ApplicationType,
                ClientUri = input.ClientUri,
                LogoUri = input.LogoUri,
            };
            input.GetPermissions().ToList().ForEach(x=> descriptor.Permissions.Add(x));
            input.RedirectUris.ToList().ForEach(x => descriptor.RedirectUris.Add(new Uri(x)));
            input.PostLogoutRedirectUris.ToList().ForEach(x => descriptor.PostLogoutRedirectUris.Add(new Uri(x)));
            await _applicationManager.CreateAsync(descriptor);

            var item = await Repository.FirstAsync(x=>x.ClientId == input.ClientId);
            return await MapToGetOutputDtoAsync(item);
        }

        [Authorize(OpenIddictPermissions.Applications.Delete)]
        public override async Task DeleteAsync(Guid id)
        {
            var item = await Repository.GetAsync(id);
            await _applicationManager.DeleteAsync(item.ToModel());
        }

        [Authorize(OpenIddictPermissions.Applications.Update)]
        public override async Task<ApplicationDto> UpdateAsync(Guid id, ApplicationCreateUpdateDto input)
        {
            var item = await Repository.GetAsync(id);
            if(item.ClientId != input.ClientId && await Repository.AnyAsync(x => x.ClientId == input.ClientId!))
            {
                throw new UserFriendlyException($"客户端ID已经存在：{input.ClientId}");
            }
            var descriptor = new AbpApplicationDescriptor
            {
                ClientId = input.ClientId,
                ClientType = input.ClientType,
                ClientSecret = input.ClientSecret,
                ConsentType = input.ConsentType,
                DisplayName = input.DisplayName,
                ApplicationType = input.ApplicationType,
                ClientUri = input.ClientUri,
                LogoUri = input.LogoUri,
            };
            input.GetPermissions().ToList().ForEach(x => descriptor.Permissions.Add(x));
            input.RedirectUris.ToList().ForEach(x => descriptor.RedirectUris.Add(new Uri(x)));
            input.PostLogoutRedirectUris.ToList().ForEach(x => descriptor.PostLogoutRedirectUris.Add(new Uri(x)));

            var m = item.ToModel();
            await _applicationManager.PopulateAsync(m, descriptor);
            await _applicationManager.UpdateAsync(m);

            item = await Repository.GetAsync(id);
            return await MapToGetOutputDtoAsync(item);
        }

        protected override async Task<IQueryable<OpenIddictApplication>> CreateFilteredQueryAsync(ApplicationPagedDto input)
        {
            var queryable = await base.CreateFilteredQueryAsync(input);
            queryable = queryable.WhereIf(!input.Filter.IsNullOrWhiteSpace(),
                    x => x.ClientId.Contains(input.Filter!) ||
                         x.DisplayName.Contains(input.Filter!)
            );
            return queryable;
        }
    }
}
